﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using Simple3DRedactor.Domain;
using Tao.OpenGl;

namespace Simple3DRedactor.OpenGLViewer
{
    public enum View
    {
        Top,
        Front,
        Left,
        Perspective
    }

    public partial class OpenGLViewer : UserControl
    {
        private readonly Dictionary<View, Renderer> _renderers = new Dictionary<View, Renderer>();

        private View _view;
        private Renderer _renderer;

        private IOpenGLObjectView _selectedItem;

        public OpenGLViewer()
        {
            InitializeComponent();

            gl.MouseWheel += OnMouseWheel;

            var top = new TopRenderer(this);
            top.SelectionChanged += OnSelectionChanged;

            var front = new FrontRenderer(this);
            front.SelectionChanged += OnSelectionChanged;

            var left = new LeftRenderer(this);
            left.SelectionChanged += OnSelectionChanged;

            var perspective = new PerspectiveRenderer(this);
            perspective.SelectionChanged += OnSelectionChanged;

            _renderers.Add(View.Top, top);
            _renderers.Add(View.Front, front);
            _renderers.Add(View.Left, left);
            _renderers.Add(View.Perspective, perspective);

            View = View.Top;
        }

        [Browsable(false)]
        public Scene Scene
        {
            get { return SceneView == null ? null : SceneView.Scene; }
            set {
                SceneView = value == null ? null : new SceneView<IOpenGLObjectView>(value, ViewFactory = new ObjectViewFactory());
            }
        }

        public View View
        {
            get { return _view; }
            set
            {
                _view = value;

                _renderer = _renderers[_view];
            }
        }

        internal SceneView<IOpenGLObjectView> SceneView { get; private set; }
        internal ObjectViewFactory ViewFactory { get; private set; }

        public event Action<Scene, SceneObject> SelectionChanged;
        private void OnSelectionChanged(IOpenGLObjectView view)
        {
            _selectedItem = view;
            if (SelectionChanged != null) SelectionChanged(Scene, SelectedObject);
        }

        public SceneObject SelectedObject
        {
            get { return _selectedItem == null ? null : _selectedItem.InnerObject; }
            set
            {
                if (SceneView == null)
                    return;

                if (value == null)
                {
                    foreach (var view2 in SceneView)
                        view2.Selected = false;

                    return;
                }

                if (_selectedItem != null && _selectedItem.InnerObject == value)
                {
                    _selectedItem.Selected = true;
                    return;
                }

                foreach (var view in SceneView)
                {
                    if (view.InnerObject == value)
                    {
                        foreach (var view2 in SceneView)
                            view2.Selected = false;

                        view.Selected = true;
                        return;
                    }
                }
            }
        }
        
        private void OnLoad(object sender, EventArgs e)
        {
            gl.InitializeContexts();

            Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_NICEST);
            Gl.glShadeModel(Gl.GL_SMOOTH);
            Gl.glEnable(Gl.GL_NORMALIZE);

            timer.Enabled = true;
        }

        private void OnPaint(object sender, PaintEventArgs e)
        {
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);

            _renderer.SetupCamera(gl.Width, gl.Height);
            
            _renderer.Show(SceneView);
        }

        private void OnResize(object sender, EventArgs e)
        {
            Gl.glViewport(0, 0, gl.Width, gl.Height);
        }
        
        private void OnMouseDown(object sender, MouseEventArgs e)
        {
            _renderer.OnMouseDown(e);
        }

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            _renderer.OnMouseMove(e);
        }

        private void OnMouseUp(object sender, MouseEventArgs e)
        {
            _renderer.OnMouseUp(e);
        }

        private void OnMouseWheel(object sender, MouseEventArgs e)
        {
            _renderer.OnMouseWheel(e);
        }

        private void OnKeyPress(object sender, KeyPressEventArgs e)
        {
            _renderer.OnKeyPress(e);
        }

        private void OnKeyDown(object sender, KeyEventArgs e)
        {
            _renderer.OnKeyDown(e);
        }

        private void TimerTick(object sender, EventArgs e)
        {
            if (SceneView != null)
                gl.Invalidate();
        }
    }
}
